package net.wanderinghorse.jsonmessage.response;

import net.wanderinghorse.json.JSONArray;
import net.wanderinghorse.json.JSONObject;
import net.wanderinghorse.jsonmessage.*;

import javax.servlet.http.Cookie;

/**
* Implements the "cookie" response type, as described in the library manual.
 *
 * Bugs and missing features:
 *
 * - The "clearCookies" option is not yet implemented.
 *
 * - The rest of it is 100% untested because i don't yet have an HTTP client implementation
 * which can be used to test this. Doing this would require using the JS front-end and connecting
 * it to the Java back-end, which i haven't yet done.
 */
public class HttpCookie extends AbstractRequestHandlerServlet {
    public HttpCookie() {
        super(true);
    }

    /**
     * Searches src for a cookie with the given name. On success it returns that cookie's
     * value.
     *
     * i have seen cookie stores where the same key may be added multiple times, appended to the
     * end of the list on changes. This function traverses backwards through the list for that
     * reason.
     *
     * TODO: add the 'clearCookies' option.
     *
     * @param src The list of cookies (may be null).
     * @param key The key to search for.
     * @return The matching cookie, or null if not found.
     */
    private Cookie searchCookies( Cookie[] src, final String key ) {
        if( (null != src) && (null!=key) ) {
            for( int i = (src.length-1); i >= 0; --i ) {
                Cookie c = src[i];
                if( (null != c) && key.equals( c.getName() ) ) {
                    return c;
                }
            }
        }
        return null;
    }

    private boolean isValueNull( final String value ) {
        return (null == value) || "null".equals(value);
    }

    @Override
    protected void doProcessResponse(final ServletInfo hreq, final Request req, final Response resp)
     throws Exception {
        JSONObject rpay = this.getPayloadObject( req );
        if( null == rpay ) {
            this.setErrorState( resp, 1, "No operations were specified!" );
            return;
        }
        JSONObject opay = new JSONObject();
        resp.setPayload( opay );

        JSONArray list = rpay.optJSONArray( "get" );
        if( null != list ) {
            // Fetch cookies...
            Cookie[] cookies = hreq.getCookies();
            JSONObject jo = new JSONObject();
            opay.put("get", jo);
            if( null != cookies ) {
                opay.put("cookieCount", cookies.length);
                final boolean doAll = (0==list.length());
                if( doAll ) {
                    for( Cookie c : cookies ) {
                        jo.put( c.getName(), ""+c.getValue() );
                    }
                }
                else {
                    for( int i = 0; i < list.length(); ++i ) {
                        final String key = list.getString(i);
                        final Cookie c = searchCookies( cookies, key );
                        if( c != null ) {
                            jo.put( key, c.getValue() );
                        }
                    }
                }
            }
        }
        JSONObject toSet = rpay.optJSONObject( "set" );
        if( null != toSet ) {
            JSONObject jo = new JSONObject();
            opay.put( "set", jo );
            JSONArray keys = toSet.names();
            for( int i = 0; i < keys.length(); ++i ) {
                final String key = keys.get(i).toString();
                final String val = toSet.optString(key, null);
                final boolean isNull = isValueNull(val);
                if( isNull ) {
                    final Cookie c = searchCookies( hreq.getCookies(), key );
                    if( null != c ) {
                        hreq.removeCookie( c );
                    }
                }
                else {
                    Cookie c = new Cookie( key, val );
                    hreq.addCookie( c );
                }
                jo.put( key, isNull ? JSONObject.NULL : val );
            }
        }
    }
}
